package com.sprite.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;


/**
 * @author Jack
 */
public final class UtilXml {

    public static Document readXmlDocument(String content)
            throws SAXException, ParserConfigurationException, java.io.IOException {
        return readXmlDocument(content, true);
    }

    public static Document readXmlDocument(String content, boolean validate)
            throws SAXException, ParserConfigurationException, java.io.IOException {
        if (content == null) {
            return null;
        }
        ByteArrayInputStream bis = new ByteArrayInputStream(content.getBytes("UTF-8"));
        return readXmlDocument(bis, validate, "Internal Content");
    }

    public static Document readXmlDocument(URL url) throws SAXException, ParserConfigurationException, IOException {
        return readXmlDocument(url, true);
    }

    public static Document readXmlDocument(URL url, boolean validate)
            throws SAXException, ParserConfigurationException, java.io.IOException {
        if (url == null) {
            return null;
        }
        InputStream is = url.openStream();
        Document document = readXmlDocument(is, validate, url.toString());
        is.close();
        return document;
    }

    public static Document readXmlDocument(InputStream is, boolean validate, String docDescription) throws ParserConfigurationException, SAXException, IOException {
        if (is == null) {
            return null;
        }

        Document document = null;

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(validate);
        factory.setNamespaceAware(true);
        factory.setAttribute("http://xml.org/sax/features/validation", validate);
        factory.setAttribute("http://apache.org/xml/features/validation/schema", validate);

        DocumentBuilder builder = factory.newDocumentBuilder();
        if (validate) {

        }

        document = builder.parse(is);

        return document;
    }

    /**
     * 获取某元素下的直接子节点
     *
     * @param element
     *         节点
     * @param childElementName
     *         子节点名称
     * @return 子节点
     */
    public static List<Element> childElementList(Element element, String childElementName) {
        if (element == null) return null;

        Node node = element.getFirstChild();

        if (node == null) return Collections.emptyList();

        List<Element> elements = new LinkedList<Element>();
        do {
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                Element childElement = (Element) node;

                if (childElementName == null || childElementName.equals(node.getNodeName())) {
                    elements.add(childElement);
                }
            }
        } while ((node = node.getNextSibling()) != null);

        return elements;
    }

    public static List<Element> childElementList(Element element) {
        return childElementList(element, null);
    }

    /**
     * 获取第一个匹配的元素
     *
     * @param element
     *         Element
     * @param childElementName
     *         节点名称
     * @return 孩子节点
     */
    public static Element childElementOne(Element element, String childElementName) {
        List<Element> cList = childElementList(element, childElementName);
        if (UtilCollection.isEmpty(cList)) {
            return null;
        }

        return cList.get(0);
    }


    /**
     * 获取某元素下的直接子节点
     *
     * @param element
     *         节点
     * @return 子节点
     */
    public static List<Node> childNodeList(Element element) {
        if (element == null) return null;

        Node node = element.getFirstChild();

        if (node == null) return Collections.emptyList();

        List<Node> elements = new LinkedList<Node>();
        do {
            elements.add(node);
        } while ((node = node.getNextSibling()) != null);

        return elements;
    }

    /**
     * 判断元素是否包含子元素
     *
     * @param element
     *         节点
     * @return 判断结果
     */
    public static boolean hasChildElement(Element element) {
        if (element == null) return false;

        Node node = element.getFirstChild();

        if (node == null) return false;

        do {
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                return true;
            }
        } while ((node = node.getNextSibling()) != null);

        return false;
    }

    /**
     * xpath 获取元素
     *
     * @param element
     *         节点
     * @param xpath
     *         xpath 表达
     * @return 节点
     * @throws Exception
     *         包含多个节点
     */
    public static Element xpathElementOne(Element element, String xpath) throws Exception {
        List<Element> list = xpathElementList(element, xpath);

        if (UtilCollection.size(list) > 1) {
            throw new Exception("un unique");
        }

        return list.get(0);
    }

    /**
     * xpath 获取元素
     *
     * @param element
     *         节点
     * @param xpath
     *         xpath表达式
     * @return 节点
     * @throws Exception
     *         xpath解析错误
     */
    public static List<Element> xpathElementList(Element element, String xpath) throws Exception {
        XPath xPath = XPathFactory.newInstance().newXPath();
        List<Node> nodes = (List<Node>) xPath.evaluate(xpath, element, XPathConstants.NODESET);
        if (UtilCollection.isEmpty(nodes)) {
            return Collections.emptyList();
        }
        List<Element> list = new LinkedList<>();
        for (Node node : nodes) {
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                list.add((Element) node);
            }
        }

        return list;
    }

    /**
     * xpath 获取元素
     *
     * @param element
     *         节点
     * @param xpath
     *         xpath表达式
     * @return 节点
     * @throws Exception
     *         节点不唯一
     */
    public static Node xpathNodeOne(Element element, String xpath) throws Exception {
        List<Node> list = xpathNodeList(element, xpath);

        if (UtilCollection.size(list) > 1) {
            throw new Exception("un unique");
        }

        return list.get(0);
    }

    /**
     * xpath 获取node
     *
     * @param element
     *         节点
     * @param xpath
     *         xpath表达式
     * @return 节点
     * @throws Exception
     *         节点不唯一
     */
    public static List<Node> xpathNodeList(Element element, String xpath) throws Exception {
        XPath xPath = XPathFactory.newInstance().newXPath();
        List<Node> nodes = (List<Node>) xPath.evaluate(xpath, element, XPathConstants.NODESET);
        if (UtilCollection.isEmpty(nodes)) {
            return Collections.emptyList();
        }

        return nodes;
    }

    private UtilXml() {
    }
}
